package com.zhengqiangblog.core;

import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.log4j.Logger;

public class RequestProcesser {

	private static final Logger logger = Logger.getLogger(RequestProcesser.class);

	private MappingHelper mappingHelper = ApplicationContext.get(MappingHelper.class);

	public RequestProcesser() {
		logger.info("RequestProcesser initializing...");
	}

	public ActionMapping mappingRequest(HttpServletRequest request, Model model) {
		String requestURI = request.getRequestURI();
		String contextPath = request.getContextPath();
		String path = requestURI.substring(contextPath.length());
		RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod());
		return mappingHelper.getActionMapping(path, requestMethod, model);
	}

	public void processForm(HttpServletRequest request, Object form) throws Exception {
		this.doProcessForm(request, form);
	}

	private void doProcessForm(HttpServletRequest request, Object form) throws Exception {
		if (ServletFileUpload.isMultipartContent(request)) { // 带文件的表单
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			String tempDir = System.getProperty("java.io.tmpdir");
			diskFileItemFactory.setRepository(new File(tempDir));
			ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
			List<FileItem> items = servletFileUpload.parseRequest(request);
			Iterator<FileItem> iterator = items.iterator();
			Map<String, String[]> parameterMap = new HashMap<String, String[]>();
			while (iterator.hasNext()) {
				FileItem item = iterator.next();
				if (item.isFormField()) {
					String name = item.getFieldName();
					String value = item.getString("UTF-8");
					if (parameterMap.containsKey(name)) {
						String[] origValues = parameterMap.get(name);
						String[] newValues = ArrayUtils.add(origValues, origValues.length, value);
						parameterMap.put(name, newValues);
					} else {
						String[] values = new String[] { value };
						parameterMap.put(name, values);
					}
				} else {
					String name = item.getFieldName();
					String capitalizedName = StringUtils.capitalize(name);
					String setterPrefix = "set";
					Method method = null;
					try {
						method = form.getClass().getMethod(setterPrefix + capitalizedName, FileItem.class);
					} catch (NoSuchMethodException e) {
						// 在Form中找不到对应的setter方法，则忽略该字段
					}
					if (method != null) {
						method.invoke(form, item);
					}
				}
			}
			this.bindForm(parameterMap, form);
		} else { // 普通表单
			Map<String, String[]> parameterMap = request.getParameterMap();
			this.bindForm(parameterMap, form);
		}
	}

	private void bindForm(Map<String, String[]> parameterMap, Object form) throws Exception {
		Class<? extends Object> formClass = form.getClass();
		Method[] methods = formClass.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			String methodName = method.getName();
			String setterPrefix = "set";
			if (methodName.startsWith(setterPrefix)) { // 只处理set开头的方法
				String fieldName = StringUtils.uncapitalize(methodName.substring(setterPrefix.length()));
				String[] paramValues = parameterMap.get(fieldName);
				if (paramValues == null) {
					continue; // 只处理请求参数集合中存在的项
				}
				Class<?>[] parameterTypes = method.getParameterTypes();
				if (parameterTypes.length != 1) {
					continue; // 只处理有一个参数的set方法
				}
				Class<?> paramClass = parameterTypes[0];
				Object args = null;
				if (paramClass.isArray()) { // 数组类型的参数
					Class<?> componentType = paramClass.getComponentType();
					args = this.convertComponentType(componentType, paramValues);
				} else { // 其它类型的参数
					String firstParamValue = paramValues[0];
					args = this.convertType(paramClass, firstParamValue);
				}
				try {
					method.invoke(form, args); // 执行set方法
				} catch (Exception e) {
					throw new Exception("Processing request parameter error.", e);
				}
			}
		}
	}

	private Object convertType(Class<?> type, String value) {
		if (value == null) {
			return null;
		}
		Object result = null;
		if (type == int.class || type == Integer.class) {
			result = NumberUtils.toInt(value);
		} else if (type == String.class) {
			result = value;
		} else if (type == long.class || type == Long.class) {
			result = NumberUtils.toLong(value);
		} else if (type == boolean.class || type == Boolean.class) {
			result = BooleanUtils.toBoolean(value);
		} else if (type == double.class || type == Double.class) {
			result = NumberUtils.toDouble(value);
		} else if (type == float.class || type == Float.class) {
			result = NumberUtils.toFloat(value);
		} else if (type == byte.class || type == Byte.class) {
			result = NumberUtils.toByte(value);
		} else if (type == short.class || type == Short.class) {
			result = NumberUtils.toShort(value);
		} else if (type == char.class || type == Character.class) {
			result = CharUtils.toChar(value);
		}
		return result;
	}

	private Object convertComponentType(Class<?> type, String[] values) {
		if (values == null) {
			return null;
		}
		int length = values.length;
		if (type == int.class) {
			int[] result = new int[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toInt(values[i]);
			}
			return result;
		} else if (type == Integer.class) {
			Integer[] result = new Integer[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toInt(values[i]);
			}
			return result;
		} else if (type == String.class) {
			return values;
		} else if (type == long.class) {
			long[] result = new long[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toLong(values[i]);
			}
			return result;
		} else if (type == Long.class) {
			Long[] result = new Long[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toLong(values[i]);
			}
			return result;

		} else if (type == boolean.class) {
			boolean[] result = new boolean[length];
			for (int i = 0; i < length; i++) {
				result[i] = BooleanUtils.toBoolean(values[i]);
			}
			return result;
		} else if (type == Boolean.class) {
			Boolean[] result = new Boolean[length];
			for (int i = 0; i < length; i++) {
				result[i] = BooleanUtils.toBoolean(values[i]);
			}
			return result;

		} else if (type == double.class) {
			double[] result = new double[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toDouble(values[i]);
			}
			return result;
		} else if (type == Double.class) {
			Double[] result = new Double[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toDouble(values[i]);
			}
			return result;

		} else if (type == float.class) {
			float[] result = new float[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toFloat(values[i]);
			}
			return result;
		} else if (type == Float.class) {
			Float[] result = new Float[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toFloat(values[i]);
			}
			return result;
		} else if (type == byte.class) {
			byte[] result = new byte[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toByte(values[i]);
			}
			return result;
		} else if (type == Byte.class) {
			Byte[] result = new Byte[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toByte(values[i]);
			}
			return result;
		} else if (type == short.class) {
			short[] result = new short[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toShort(values[i]);
			}
			return result;
		} else if (type == Short.class) {
			Short[] result = new Short[length];
			for (int i = 0; i < length; i++) {
				result[i] = NumberUtils.toShort(values[i]);
			}
			return result;
		} else if (type == char.class) {
			char[] result = new char[length];
			for (int i = 0; i < length; i++) {
				result[i] = CharUtils.toChar(values[i]);
			}
			return result;
		} else if (type == Character.class) {
			Character[] result = new Character[length];
			for (int i = 0; i < length; i++) {
				result[i] = CharUtils.toChar(values[i]);
			}
			return result;
		}
		return null;
	}

}
